Overview

Eldritch is a Pythonic red team Domain Specific Language (DSL) based on starlark. It uses and supports most python syntax and basic functionality such as list comprehension, string operations (lower(), join(), replace(), etc.), and built-in methods (any(), dir(), sorted(), etc.). For more details on the supported functionality not listed here, please consult the Starlark Spec Reference, but for the most part you can treat this like basic Python with extra red team functionality.

Eldritch is a small interpreter that can be embedded into a c2 agent as it is with Golem and Imix. By embedding the interpreter into the agent conditional logic can be quickly evaluated without requiring multiple callbacks.

Eldritch is currently under active development to help delineate methods in development the description contains the phrase X method will.

Trying to create a tome? Check out the guide in Golem.

Examples

Kill a specific process name

for p in process.list():
    if p['name'] == "golem":
        process.kill(p['pid'])

Copy your current executable somewhere else

cur_bin_path = process.info()['exe']
dest_path = '/tmp/win'
file.copy(cur_bin_path, dest_path)
file.remove(cur_bin_path)

Parse a JSON file

json_str = file.read("/tmp/config.json")
config_data = crypto.from_json(json_str)
print(config_data['key1'])

Data types

Eldritch currently only supports the default starlark data types.

Error handling

Eldritch doesn’t implement any form of error handling. If a function fails it will stop the tome from completing execution. There is no way to recover after a function has errored.

If you’re using a function that has a chance to error (functions that do file / network IO) test preemptively with function like is_file, is_dir, is_windows, etc.

For example:

def read_passwd():
    if is_linux():
        if is_file("/etc/passwd"):
            file.read("/etc/passwd")
read_passwd()
def write_systemd_service():
    if is_linux():
        if is_dir("/lib/systemd/system/"):
            service_args = {
                "name":"my-service",
                "desc":"A test",
                "binary_path":"/bin/false",
            }
            assets.copy("systemd-template.j2", "/tmp/systemd-template.j2")
            file.template("/tmp/systemd-template.j2","/lib/systemd/system/myservice.service",args,False)
            file.remove("/tmp/systemd-template.j2")

write_systemd_service()

Standard Library

The standard library is the default functionality that eldritch provides. It contains the following libraries:

  • assets - Used to interact with files stored natively in the agent.
  • crypto - Used to encrypt/decrypt or hash data.
  • file - Used to interact with files on the system.
  • http - Used to make http(s) requests from the agent.
  • pivot - Used to identify and move between systems.
  • process - Used to interact with processes on the system.
  • random - Used to generate cryptographically secure random values.
  • regex - Regular expression capabilities for operating on strings.
  • report - Structured data reporting capabilities.
  • sys - General system capabilities can include loading libraries, or information about the current context.
  • time - General functions for obtaining and formatting time, also add delays into code.

🚨 DANGER 🚨: Name shadowing

Do not use the standard library names as local variables as it will prevent you from accessing library functions. For example, if you do:

for file in file.list("/home/"):
    print(file["file_name"])

The file library will become inaccessible.

It may even raise an error: error: Local variable 'file' referenced before assignment

Instead we recommend using more descriptive names like:

for user_home_dir in file.list("/home/"):
    print(user_home_dir["file_name"])

Assets

assets.copy

assets.copy(src: str, dst: str) -> None

The assets.copy method copies an embedded file from the agent to disk. The src variable will be the path from the embed_files_golem_prod as the root dir. For example embed_files_golem_prod/sliver/agent-x64 can be referenced as sliver/agent-x64. If dst exists it will be overwritten. If it doesn’t exist the function will fail.

def deploy_agent():
    if file.is_dir("/usr/bin"):
        assets.copy("sliver/agent-x64","/usr/bin/notsu")
        sys.exec("/usr/bin/notsu",[],true)
deploy_agent()

assets.list

assets.list() -> List<str>

The assets.list method returns a list of asset names that the agent is aware of.

assets.read_binary

assets.read_binary(src: str) -> List<int>

The assets.read_binary method returns a list of u32 numbers representing the asset files bytes.

assets.read

assets.read(src: str) -> str

The assets.read method returns a UTF-8 string representation of the asset file.


Crypto

crypto.aes_decrypt_file

crypto.aes_decrypt_file(src: str, dst: str, key: str) -> None

The crypto.aes_decrypt_file method decrypts the given src file using the given key and writes it to disk at the dst location.

Key must be 16 Bytes (Characters)

crypto.aes_encrypt_file

crypto.aes_encrypt_file(src: str, dst: str, key: str) -> None

The crypto.aes_encrypt_file method encrypts the given src file, encrypts it using the given key and writes it to disk at the dst location.

Key must be 16 Bytes (Characters)

crypto.encode_b64

crypto.encode_b64(content: str, encode_type: Optional<str>) -> str

The crypto.encode_b64 method encodes the given text using the given base64 encoding method. Valid methods include:

  • STANDARD (default)
  • STANDARD_NO_PAD
  • URL_SAFE
  • URL_SAFE_NO_PAD

crypto.decode_b64

crypto.decode_b64(content: str, decode_type: Optional<str>) -> str

The crypto.decode_b64 method encodes the given text using the given base64 decoding method. Valid methods include:

  • STANDARD (default)
  • STANDARD_NO_PAD
  • URL_SAFE
  • URL_SAFE_NO_PAD

crypto.from_json

crypto.from_json(content: str) -> Value

The crypto.from_json method converts JSON text to an object of correct type.

crypto.from_json("{\"foo\":\"bar\"}")
{
    "foo": "bar"
}

crypto.hash_file

crypto.hash_file(file: str, algo: str) -> str

The crypto.hash_file method will produce the hash of the given file’s contents. Valid algorithms include:

  • MD5
  • SHA1
  • SHA256
  • SHA512

crypto.to_json

crypto.to_json(content: Value) -> str

The crypto.to_json method converts given type to JSON text.

crypto.to_json({"foo": "bar"})
"{\"foo\":\"bar\"}"

File

file.append

file.append(path: str, content: str) -> None

The file.append method appends the content to file at path. If no file exists at path create the file with the content.

file.compress

file.compress(src: str, dst: str) -> None

The file.compress method compresses a file using the gzip algorithm. If the destination file doesn’t exist it will be created. If the source file doesn’t exist an error will be thrown. If the source path is a directory the contents will be placed in a tar archive and then compressed.

file.copy

file.copy(src: str, dst: str) -> None

The file.copy method copies a file from src path to dst path. If dst file doesn’t exist it will be created.

file.exists

file.exists(path: str) -> bool

The file.exists method checks if a file or directory exists at the path specified.

file.follow

file.follow(path: str, fn: function(str)) -> None

The file.follow method will call fn(line) for any new line that is added to the file (such as from bash_history and other logs).

# Print every line added to bob's bash history
file.follow('/home/bob/.bash_history', print)

file.is_dir

file.is_dir(path: str) -> bool

The file.is_dir method checks if a path exists and is a directory. If it doesn’t exist or is not a directory it will return False.

file.is_file

file.is_file(path: str) -> bool

The file.is_file method checks if a path exists and is a file. If it doesn’t exist or is not a file it will return False.

file.list

file.list(path: str) -> List<Dict>

The file.list method returns a list of files at the specified path. The path is relative to your current working directory and can be traversed with ../. This function also supports globbing with * for example:

file.list("/home/*/.bash_history") # List all files called .bash_history in sub dirs of `/home/`
file.list("/etc/*ssh*") # List the contents of all dirs that have `ssh` in the name and all files in etc with `ssh` in the name

Each file is represented by a Dict type. Here is an example of the Dict layout:

[
    {
        "file_name": "implants",
        "absolute_path": "/workspace/realm/implants",
        "size": 4096,
        "owner": "root",
        "group": "0",
        "permissions": "40755",
        "modified": "2023-07-09 01:35:40 UTC",
        "type": "Directory"
    },
    {
        "file_name": "README.md",
        "absolute_path": "/workspace/realm/README.md",
        "size": 750,
        "owner": "root",
        "group": "0",
        "permissions": "100644",
        "modified": "2023-07-08 02:49:47 UTC",
        "type": "File"
    },
    {
        "file_name": ".git",
        "absolute_path": "/workspace/realm/.git",
        "size": 4096,
        "owner": "root",
        "group": "0",
        "permissions": "40755",
        "modified": "2023-07-10 21:14:06 UTC",
        "type": "Directory"
    }
]

file.mkdir

file.mkdir(path: str, parent: Option<bool>) -> None

The file.mkdir method will make a new directory at path. If the parent directory does not exist or the directory cannot be created, it will error; unless the parent parameter is passed as True.

file.moveto

file.moveto(src: str, dst: str) -> None

The file.moveto method moves a file or directory from src to dst. If the dst directory or file exists it will be deleted before being replaced to ensure consistency across systems.

file.parent_dir

file.parent_dir(path: str) -> str

The file.parent_dir method returns the parent directory of a give path. Eg /etc/ssh/sshd_config -> /etc/ssh

file.read

file.read(path: str) -> str

The file.read method will read the contents of a file. If the file or directory doesn’t exist the method will error to avoid this ensure the file exists, and you have permission to read it. This function supports globbing with * for example:

file.read("/home/*/.bash_history") # Read all files called .bash_history in sub dirs of `/home/`
file.read("/etc/*ssh*") # Read the contents of all files that have `ssh` in the name. Will error if a dir is found.

file.remove

file.remove(path: str) -> None

The file.remove method deletes a file or directory (and it’s contents) specified by path.

file.replace

file.replace(path: str, pattern: str, value: str) -> None

The file.replace method finds the first string matching a regex pattern in the specified file and replaces them with the value. Please consult the Rust Regex Docs for more information on pattern matching.

file.replace_all

file.replace_all(path: str, pattern: str, value: str) -> None

The file.replace_all method finds all strings matching a regex pattern in the specified file and replaces them with the value. Please consult the Rust Regex Docs for more information on pattern matching.

file.tmp_file

file.tmp_file(name: Option<str>) -> str

The file.temp method returns the path of a new temporary file with a random filename or the optional filename provided as an argument.

file.template

file.template(template_path: str, dst: str, args: Dict<String, Value>, autoescape: bool) -> None

The file.template method reads a Jinja2 template file from disk, fill in the variables using args and then write it to the destination specified. If the destination file doesn’t exist it will be created (if the parent directory exists). If the destination file does exist it will be overwritten. The args dictionary currently supports values of: int, str, and List. autoescape when True will perform HTML character escapes according to the OWASP XSS guidelines

file.timestomp

file.timestomp(src: str, dst: str) -> None

Unimplemented.

file.write

file.write(path: str, content: str) -> None

The file.write method writes to a given file path with the given content. If a file or directory already exists at this path, the method will fail.

file.find

file.find(path: str, name: Option<str>, file_type: Option<str>, permissions: Option<int>, modified_time: Option<int>, create_time: Option<int>) -> List<str>

The file.find method finds all files matching the used parameters. Returns file path for all matching items.

  • name: Checks if file name contains provided input
  • file_type: Checks for ‘file’ or ‘dir’ for files or directories, respectively.
  • permissions: On UNIX systems, takes numerical input of standard unix permissions (rwxrwxrwx == 777). On Windows, takes 1 or 0, 1 if file is read only.
  • modified_time: Checks if last modified time matches input specified in time since EPOCH
  • create_time: Checks if last modified time matches input specified in time since EPOCH

HTTP

The HTTP library also allows the user to allow the http client to ignore TLS validation via the allow_insecure optional parameter (defaults to false).

http.download

http.download(uri: str, dst: str, allow_insecure: Option<bool>) -> None

The http.download method downloads a file at the URI specified in uri to the path specified in dst. If a file already exists at that location, it will be overwritten.

http.get

http.get(uri: str, query_params: Option<Dict<str, str>>, headers: Option<Dict<str, str>>, allow_insecure: Option<bool>) -> str

The http.get method sends an HTTP GET request to the URI specified in uri with the optional query paramters specified in query_params and headers specified in headers, then return the response body as a string. Note: in order to conform with HTTP2+ all header names are transmuted to lowercase.

http.post

http.post(uri: str, body: Option<str>, form: Option<Dict<str, str>>, headers: Option<Dict<str, str>>, allow_insecure: Option<bool>) -> str

The http.post method sends an HTTP POST request to the URI specified in uri with the optional request body specified by body, form paramters specified in form, and headers specified in headers, then return the response body as a string. Note: in order to conform with HTTP2+ all header names are transmuted to lowercase. Other Note: if a body and a form are supplied the value of body will be used.


Pivot

pivot.arp_scan

pivot.arp_scan(target_cidrs: List<str>) -> List<str>

The pivot.arp_scan method is for enumerating hosts on the agent network without using TCP connect or ping.

  • target_cidrs must be in a CIDR format eg. 127.0.0.1/32. Domains and single IPs example.com / 127.0.0.1 cannot be passed.
  • Must be running as root to use.
  • Not supported on Windows

Results will be in the format:

$> pivot.arp_scan(["192.168.1.1/32"])

Success

[
    { "ip": "192.168.1.1", "mac": "ab:cd:ef:01:23:45", "interface": "eno0" }
]

Failure

[]

pivot.bind_proxy

pivot.bind_proxy(listen_address: str, listen_port: int, username: str, password: str ) -> None

The pivot.bind_proxy method is being proposed to provide users another option when trying to connect and pivot within an environment. This function will start a SOCKS5 proxy on the specified port and interface, with the specified username and password (if provided).

pivot.ncat

pivot.ncat(address: str, port: int, data: str, protocol: str ) -> str

The pivot.ncat method allows a user to send arbitrary data over TCP/UDP to a host. If the server responds that response will be returned.

protocol must be tcp, or udp anything else will return an error Protocol not supported please use: udp or tcp..

pivot.port_forward

pivot.port_forward(listen_address: str, listen_port: int, forward_address: str, forward_port: int, str: protocol ) -> None

The pivot.port_forward method is being proposed to provide socat like functionality by forwarding traffic from a port on a local machine to a port on a different machine allowing traffic to be relayed.

pivot.port_scan

pivot.port_scan(target_cidrs: List<str>, ports: List<int>, protocol: str, timeout: int) -> List<str>

The pivot.port_scan method allows users to scan TCP/UDP ports within the eldritch language. Inputs:

  • target_cidrs must be in a CIDR format eg. 127.0.0.1/32. Domains and single IPs example.com / 127.0.0.1 cannot be passed.
  • ports can be a list of any number of integers between 1 and 65535.
  • protocol must be: tcp or udp. These are the only supported options.
  • timeout is the number of seconds a scan will wait without a response before it’s marked as timeout

Results will be in the format:

[
    { "ip": "127.0.0.1", "port": 22, "protocol": "tcp", "status": "open"},
    { "ip": "127.0.0.1", "port": 21, "protocol": "tcp", "status": "closed"},
    { "ip": "127.0.0.1", "port": 80, "protocol": "tcp", "status": "timeout"},
]

A ports status can be open, closed, or timeout:

State Protocol Meaning
open tcp Connection successful.
close tcp Connection refused.
timeout tcp Connection dropped or didn’t respond.
open udp Connection returned some data.
timeout udp Connection was refused, dropped, or didn’t respond.

Each IP in the specified CIDR will be returned regardless of if it returns any open ports. Be mindful of this when scanning large CIDRs as it may create large return objects.

NOTE: Windows scans against localhost/127.0.0.1 can behave unexpectedly or even treat the action as malicious. Eg. scanning ports 1-65535 against windows localhost may cause the stack to overflow or process to hang indefinitely.

pivot.reverse_shell_pty

pivot.reverse_shell_pty(cmd: Optional<str>) -> None

The pivot.reverse_shell_pty method spawns the provided command in a cross-platform PTY and opens a reverse shell over the agent’s current transport (e.g. gRPC). If no command is provided, Windows will use cmd.exe. On other platforms, /bin/bash is used as a default, but if it does not exist then /bin/sh is used.

pivot.smb_exec

pivot.smb_exec(target: str, port: int, username: str, password: str, hash: str, command: str) -> str

The pivot.smb_exec method is being proposed to allow users a way to move between hosts running smb.

pivot.ssh_copy

pivot.ssh_copy(target: str, port: int, src: str, dst: str, username: str, password: Optional<str>, key: Optional<str>, key_password: Optional<str>, timeout: Optional<int>) -> None

The pivot.ssh_copy method copies a local file to a remote system. If no password or key is specified the function will error out with: Failed to run handle_ssh_exec: Failed to authenticate to host If the connection is successful but the copy writes a file error will be returned.

ssh_copy will first delete the remote file and then write to its location. The file directory the dst file exists in must exist in order for ssh_copy to work.

pivot.ssh_exec

pivot.ssh_exec(target: str, port: int, command: str, username: str, password: Optional<str>, key: Optional<str>, key_password: Optional<str>, timeout: Optional<int>) -> List<Dict>

The pivot.ssh_exec method executes a command string on the remote host using the default shell. If no password or key is specified the function will error out with: Failed to run handle_ssh_exec: Failed to authenticate to host If the connection is successful but the command fails no output will be returned but the status code will be set. Not returning stderr is a limitation of the way we’re performing execution. Since it’s not using the SSH shell directive we’re limited on the return output we can capture.

{
    "stdout": "uid=1000(kali) gid=1000(kali) groups=1000(kali),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),109(netdev),118(bluetooth),128(lpadmin),132(scanner),143(docker)\n",
    "status": 0
}

pivot.ssh_password_spray

pivot.ssh_password_spray(targets: List<str>, port: int, credentials: List<str>, keys: List<str>, command: str, shell_path: str) -> List<str>

The pivot.ssh_password_spray method is being proposed to allow users a way to test found credentials against neighboring targets. It will iterate over the targets list and try each credential set. Credentials will be a formatted list of usernames and passwords Eg. “username:password”. The function will return a formatted list of “target:username:password”. command and shell_path is intended to give more flexibility but may be adding complexity.


Process

process.info

process.info(pid: Optional<int>) -> Dict

The process.info method returns all information on a given process ID. Default is the current process.

{
  "pid": 1286574,
  "name": "golem",
  "cmd": [
    "./target/debug/golem",
    "-i"
  ],
  "exe": "/home/user/realm/implants/target/debug/golem",
  "environ": [
    "USER=user",
    "HOME=/home/user",
    "PATH=/home/user/.cargo/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/snap/bin:/home/user/.dotnet/tools",
    "SHELL=/bin/zsh",
    "TERM=xterm-256color",
    "SSH_TTY=/dev/pts/0",
    "SHLVL=1",
    "PWD=/home/user",
    "OLDPWD=/home/user",
    "XDG_DATA_DIRS=/usr/local/share:/usr/share:/var/lib/snapd/desktop",
    "P9K_TTY=old",
    "_P9K_TTY=/dev/pts/0",
    "ZSH=/home/user/.oh-my-zsh",
  ],
  "cwd": "/home/user/realm/implants",
  "root": "/",
  "memory_usage": 32317440,
  "virtual_memory_usage": 1712074752,
  "ppid": 1180405,
  "status": "Sleeping",
  "start_time": 1698106833,
  "run_time": 2,
  "uid": 1000,
  "euid": 1000,
  "gid": 1000,
  "egid": 1000,
  "sid": 1180405
}

process.kill

process.kill(pid: int) -> None

The process.kill method will kill a process using the KILL signal given its process id.

process.list

process.list() -> List<Dict>

The process.list method returns a list of dictionaries that describe each process. The dictionaries follow the schema:

{
    "pid": "9812",
    "ppid": "1",
    "status": "Sleeping",
    "name": "golem",
    "path": "/usr/bin/golem",
    "username": "root",
    "command": "/usr/bin/golem -i",
    "cwd": "/root/",
    "environ": "CARGO_PKG_REPOSITORY= CARGO_PKG_RUST_VERSION= CARGO_PKG_VERSION=0.1.0 CARGO_PKG_VERSION_MAJOR=0",
}

process.name

process.name(pid: int) -> str

The process.name method returns the name of the process from it’s given process id.

process.netstat

process.netstat() -> List<Dict>

The process.netstat method returns all information on TCP, UDP, and Unix sockets on the system. Will also return PID and Process Name of attached process, if one exists.

Currently only shows LISTENING TCP connections

[
    {
        "socket_type": "TCP",
        "local_address": "127.0.0.1",
        "local_port": 46341,
        "pid": 2359037
    },
    ...
]

Random

The random library is designed to enable generation of cryptogrphically secure random vaules. None of these functions will be blocking.

random.bool

random.bool() -> bool

The random.bool method returns a randomly sourced boolean value.

random.string

random.string(length: uint, charset: Optional<str>) -> str The random.string method returns a randomly generated string of the specified length. If charset is not provided defaults to Alphanumeric. Warning, the string is stored entirely in memory so exceptionally large files (multiple megabytes) can lead to performance issues.


Regex

The regex library is designed to enable basic regex operations on strings. Be aware as the underlying implementation is written in Rust we rely on the Rust Regex Syntax as talked about here. Further, we only support a single capture group currently, defining more/less than one will cause the tome to error.

regex.match_all

regex.match_all(haystack: str, pattern: str) -> List<str>

The regex.match_all method returns a list of capture group strings that matched the given pattern within the given haystack. Please consult the Rust Regex Docs for more information on pattern matching.

regex.match

regex.match(haystack: str, pattern: str) -> str

The regex.match method returns the first capture group string that matched the given pattern within the given haystack. Please consult the Rust Regex Docs for more information on pattern matching.

regex.replace_all

regex.replace_all(haystack: str, pattern: str, value: string) -> str

The regex.replace_all method returns the given haystack with all the capture group strings that matched the given pattern replaced with the given value. Please consult the Rust Regex Docs for more information on pattern matching.

regex.replace

regex.replace(haystack: str, pattern: str, value: string) -> str

The regex.replace method returns the given haystack with the first capture group string that matched the given pattern replaced with the given value. Please consult the Rust Regex Docs for more information on pattern matching.


Report

The report library is designed to enable reporting structured data to Tavern. It’s API is still in the active development phase, so future versions of Eldritch may break tomes that rely on this API.

report.file

report.file(path: str) -> None

Reports a file from the host that an Eldritch Tome is being evaluated on (e.g. a compromised system) to Tavern. It has a 1GB size limit, and will report the file in 1MB chunks. This process happens asynchronously, so after report.file() returns there are no guarantees about when this file will be reported. This means that if you delete the file immediately after reporting it, it may not be reported at all (race condition).

report.process_list

report.process_list(list: List<Dict>) -> None

Reports a snapshot of the currently running processes on the host system. This should only be called with the entire process list (e.g. from calling process.list()), as it will replace Tavern’s current list of processes for the host with this new snapshot.

report.ssh_key

report.ssh_key(username: str, key: str) -> None

Reports a captured SSH Key credential to Tavern. It will automatically be associated with the host that the Eldritch Tome was being evaluated on.

report.user_password

report.user_password(username: str, password: str) -> None

Reports a captured username & password combination to Tavern. It will automatically be associated with the host that the Eldritch Tome was being evaluated on.


Sys

sys.dll_inject

sys.dll_inject(dll_path: str, pid: int) -> None

The sys.dll_inject method will attempt to inject a dll on disk into a remote process by using the CreateRemoteThread function call.

sys.dll_reflect

sys.dll_reflect(dll_bytes: List<int>, pid: int, function_name: str) -> None

The sys.dll_reflect method will attempt to inject a dll from memory into a remote process by using the loader defined in realm/bin/reflective_loader.

The ints in dll_bytes will be cast down from int u32 —> u8 in rust. If your dll_bytes array contains a value greater than u8::MAX it will cause the function to fail. If you’re doing any decryption in starlark make sure to be careful of the u8::MAX bound for each byte.

sys.exec

sys.exec(path: str, args: List<str>, disown: Optional<bool>) -> Dict

The sys.exec method executes a program specified with path and passes the args list. Disown will run the process in the background disowned from the agent. This is done through double forking and only works on *nix systems.

sys.exec("/bin/bash",["-c", "whoami"])
{
    "stdout":"root\n",
    "stderr":"",
    "status":0,
}
sys.exec("/bin/bash",["-c", "ls /nofile"])
{
    "stdout":"",
    "stderr":"ls: cannot access '/nofile': No such file or directory\n",
    "status":2,
}

sys.get_env

sys.get_env() -> Dict

The sys.get_env method returns a dictionary that describes the current process’s environment variables. An example is below:

{
    "FOO": "BAR",
    "CWD": "/"
}

sys.get_ip

sys.get_ip() -> List<Dict>

The sys.get_ip method returns a list of network interfaces as a dictionary. An example is available below:

[
    {
        "name": "eth0",
        "ips": [
            "172.17.0.2/24"
        ],
        "mac": "02:42:ac:11:00:02"
    },
    {
        "name": "lo",
        "ips": [
            "127.0.0.1/8"
        ],
        "mac": "00:00:00:00:00:00"
    }
]

sys.get_os

sys.get_os() -> Dict

The sys.get_os method returns a dictionary that describes the current systems OS. An example is below:

{
    "arch": "x86_64",
    "desktop_env": "Unknown: Unknown",
    "distro": "Debian GNU/Linux 10 (buster)",
    "platform": "PLATFORM_LINUX"
}

sys.get_pid

sys.get_pid() -> int

The sys.get_pid method returns the process ID of the current process. An example is below:

$> sys.get_pid()
123456

sys.get_reg

sys.get_reg(reghive: str, regpath: str) -> Dict

The sys.get_reg method returns the registry values at the requested registry path. An example is below:

$> sys.get_reg("HKEY_LOCAL_MACHINE","SOFTWARE\\Microsoft\\Windows\\CurrentVersion")
{
    "ProgramFilesDir": "C:\\Program Files",
    "CommonFilesDir": "C:\\Program Files\\Common Files",
    "ProgramFilesDir (x86)": "C:\\Program Files (x86)",
    "CommonFilesDir (x86)": "C:\\Program Files (x86)\\Common Files",
    "CommonW6432Dir": "C:\\Program Files\\Common Files",
    "DevicePath": "%SystemRoot%\\inf",
    "MediaPathUnexpanded": "%SystemRoot%\\Media",
    "ProgramFilesPath": "%ProgramFiles%",
    "ProgramW6432Dir": "C:\\Program Files",
    "SM_ConfigureProgramsName": "Set Program Access and Defaults",
    "SM_GamesName": "Games"
}

sys.get_user

sys.get_user() -> Dict

The sys.get_user method returns a dictionary that describes the current process’s running user. On *Nix, will return UID, EUID, GID, EGID, and detailed user info for the UID and EUID mappings. For users, will return name and groups of user.

{
    "uid": {
        "uid": 0,
        "name": "root",
        "gid": 0,
        "groups": ["root"]
    },
    "euid": {
        "uid": 0,
        "name": "root",
        "gid": 0,
        "groups": ["root"]
    },
    "gid": 0,
    "egid": 0
}

sys.hostname

sys.hostname() -> String

The sys.hostname method returns a String containing the host’s hostname.

sys.is_bsd

sys.is_bsd() -> bool

The sys.is_bsd method returns True if on a freebsd, netbsd, or openbsd system and False on everything else.

sys.is_linux

sys.is_linux() -> bool

The sys.is_linux method returns True if on a linux system and False on everything else.

sys.is_macos

sys.is_macos() -> bool

The sys.is_macos method returns True if on a mac os system and False on everything else.

sys.is_windows

sys.is_windows() -> bool

The sys.is_windows method returns True if on a windows system and False on everything else.

sys.shell

sys.shell(cmd: str) -> Dict

The sys.shell Given a string run it in a native interpreter. On MacOS, Linux, and *nix/bsd systems this is /bin/bash -c <your command>. On Windows this is cmd /C <your command>. Stdout, stderr, and the status code will be returned to you as a dictionary with keys: stdout, stderr, status. For example:

sys.shell("whoami")
{
    "stdout":"root\n",
    "stderr":"",
    "status":0,
}
sys.shell("ls /nofile")
{
    "stdout":"",
    "stderr":"ls: cannot access '/nofile': No such file or directory\n",
    "status":2,
}

sys.write_reg_hex

sys.write_reg_hex(reghive: str, regpath: str, regname: str, regtype: str, regvalue: str) -> Bool

The sys.write_reg_hex method returns True if registry values are written to the requested registry path and accepts a hexstring as the value argument. An example is below:

$> sys.write_reg_hex("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_SZ","deadbeef")
True
$> sys.write_reg_hex("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_BINARY","deadbeef")
True
$> sys.write_reg_hex("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_NONE","deadbeef")
True
$> sys.write_reg_hex("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_EXPAND_SZ","deadbeef")
True
$> sys.write_reg_hex("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_DWORD","deadbeef")
True
$> sys.write_reg_hex("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_DWORD_BIG_ENDIAN","deadbeef")
True
$> sys.write_reg_hex("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_LINK","deadbeef")
True
$> sys.write_reg_hex("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_MULTI_SZ","dead,beef")
True
$> sys.write_reg_hex("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_RESOURCE_LIST","deadbeef")
True
$> sys.write_reg_hex("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_FULL_RESOURCE_DESCRIPTOR","deadbeef")
True
$> sys.write_reg_hex("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_RESOURCE_REQUIREMENTS_LIST","deadbeef")
True
$> sys.write_reg_hex("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_QWORD","deadbeefdeadbeef")
True

sys.write_reg_int

sys.write_reg_int(reghive: str, regpath: str, regname: str, regtype: str, regvalue: int) -> Bool

The sys.write_reg_int method returns True if registry values are written to the requested registry path and accepts an integer as the value argument. An example is below:

$> sys.write_reg_int("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_SZ",12345678)
True
$> sys.write_reg_int("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_BINARY",12345678)
True
$> sys.write_reg_int("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_NONE",12345678)
True
$> sys.write_reg_int("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_EXPAND_SZ",12345678)
True
$> sys.write_reg_int("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_DWORD",12345678)
True
$> sys.write_reg_int("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_DWORD_BIG_ENDIAN",12345678)
True
$> sys.write_reg_int("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_LINK",12345678)
True
$> sys.write_reg_int("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_MULTI_SZ",12345678)
True
$> sys.write_reg_int("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_RESOURCE_LIST",12345678)
True
$> sys.write_reg_int("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_FULL_RESOURCE_DESCRIPTOR",12345678)
True
$> sys.write_reg_int("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_RESOURCE_REQUIREMENTS_LIST",12345678)
True
$> sys.write_reg_int("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_QWORD",12345678)
True

sys.write_reg_str

sys.write_reg_str(reghive: str, regpath: str, regname: str, regtype: str, regvalue: str) -> Bool

The sys.write_reg_str method returns True if registry values are written to the requested registry path and accepts a string as the value argument. An example is below:

$> sys.write_reg_str("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_SZ","BAR1")
True
$> sys.write_reg_str("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_BINARY","DEADBEEF")
True
$> sys.write_reg_str("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_NONE","DEADBEEF")
True
$> sys.write_reg_str("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_EXPAND_SZ","BAR2")
True
$> sys.write_reg_str("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_DWORD","12345678")
True
$> sys.write_reg_str("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_DWORD_BIG_ENDIAN","12345678")
True
$> sys.write_reg_str("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_LINK","A PLAIN STRING")
True
$> sys.write_reg_str("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_MULTI_SZ","BAR1,BAR2,BAR3")
True
$> sys.write_reg_str("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_RESOURCE_LIST","DEADBEEF")
True
$> sys.write_reg_str("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_FULL_RESOURCE_DESCRIPTOR","DEADBEEF")
True
$> sys.write_reg_str("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_RESOURCE_REQUIREMENTS_LIST","DEADBEEF")
True
$> sys.write_reg_str("HKEY_CURRENT_USER","SOFTWARE\\TEST1","FOO1","REG_QWORD","1234567812345678")
True

Time

time.format_to_epoch

time.format_to_epoch(input: str, format: str) -> int

The time.format_to_epoch method returns the seconds since epoch for the given UTC timestamp of the provided format. Input must include date and time components.

Some common formatting methods are:

  • “%Y-%m-%d %H:%M:%S” (24 Hour Time)
  • “%Y-%m-%d %I:%M:%S %P” (AM/PM)

For reference on all available format specifiers, see https://docs.rs/chrono/latest/chrono/format/strftime/index.html

time.format_to_readable

time.format_to_readable(input: int, format: str) -> str

The time.format_to_readable method returns the timestamp in the provided format of the provided UTC timestamp.

Some common formatting methods are:

  • “%Y-%m-%d %H:%M:%S” (24 Hour Time)
  • “%Y-%m-%d %I:%M:%S %P” (AM/PM)

For reference on all available format specifiers, see https://docs.rs/chrono/latest/chrono/format/strftime/index.html

time.now

time.now() -> int

The time.now method returns the time since UNIX EPOCH (Jan 01 1970). This uses the local system time.

time.sleep

time.sleep(secs: float)

The time.sleep method sleeps the task for the given number of seconds.